﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace PuyoNeuron
{
    public class TextEditor : IUIElement
    {
        public delegate void ReturnValue(string pReturn);

        Vector2 mTopLeft;
        Vector2 mSize;
        bool mDispose;
        Keys[] mPreviousKeys;
        int mCursor;
        int mKeyRepeat;
        Keys? mRepeatingKey;
        float mCursorX;

        string mText;
        ReturnValue mOnReturn;

        static Texture2D sTextEditorTexture;
        static Texture2D sCursorTexture;

        public int Cursor
        {
            get { return mCursor; }
            set
            {
                if (value < 0)
                    mCursor = 0;
                else if (value > mText.Length)
                    mCursor = mText.Length;
                else
                    mCursor = value;
            }
        }

        public Vector2 TopLeft
        {
            get { return mTopLeft; }
            set { mTopLeft = value; }
        }

        public Vector2 Size
        {
            get { return mSize; }
            set { mSize = new Vector2((value.X <= 80) ? 80 : (int)value.X, (value.Y <= 40) ? 40 : (int)value.Y); }
        }

        public bool Dispose
        {
            get { return mDispose; }
            set { mDispose = value; }
        }

        public static Texture2D TextEditorTexture
        {
            get { return sTextEditorTexture; }
            set { sTextEditorTexture = value; }
        }

        public static Texture2D CursorTexture
        {
            get { return sCursorTexture; }
            set { sCursorTexture = value; }
        }

        public TextEditor(string pInitialText, ReturnValue pOnReturn)
        {
            mText = pInitialText;
            mOnReturn = pOnReturn;
            Cursor = mText.Length;
        }

        public void Update()
        {
            Keys[] lKeys = Keyboard.GetState().GetPressedKeys();

            mKeyRepeat--;
            if (lKeys.Length == 0)
            {
                mRepeatingKey = null;
                mKeyRepeat = 0;
            }

            foreach (Keys lKey in lKeys)
            {
                if (!mPreviousKeys.Contains(lKey) || mKeyRepeat <= 0)
                {
                    // key repeat
                    if (!mRepeatingKey.HasValue || mRepeatingKey.Value != lKey)
                    {
                        mRepeatingKey = lKey;
                        mKeyRepeat = 15;
                    }
                    else
                    {
                        mKeyRepeat = 2;
                    }

                    if (lKey == Keys.Home)
                    {
                        Cursor--;
                        int i = Cursor;
                        while (i > 0 && mText[i] != '\n')
                            i--;
                        Cursor = i == 0 ? 0 : i + 1;
                        UpdateCursorX();
                    }
                    else if (lKey == Keys.End)
                    {
                        int i = Cursor;
                        while (i < mText.Length && mText[i] != '\n')
                            i++;
                        Cursor = i;
                        UpdateCursorX();
                    }
                    else if (lKey == Keys.Down)
                    {
                        // find next line
                        while (Cursor < mText.Length && mText[Cursor] != '\n')
                            Cursor++;

                        // pass next line
                        Cursor++;

                        while (Cursor < mText.Length &&
                               mText[Cursor] != '\n' &&
                               mCursorX > Panel.LabelFont.MeasureString(mText.Substring(0, Cursor).Split('\n').Last()).X)
                            Cursor++;

                        if (Cursor == mText.Length)
                            UpdateCursorX();
                    }
                    else if (lKey == Keys.Up)
                    {
                        // if we're on the top line, just go to position zero
                        if (mText.Substring(0, Cursor).Split('\n').Length == 1)
                        {
                            Cursor = 0;
                            UpdateCursorX();
                        }
                        else
                        {
                            Cursor--;

                            // find previous line
                            while (Cursor > 0 && mText[Cursor] != '\n')
                                Cursor--;

                            // pass previous line
                            Cursor--;

                            // find next previous line
                            while (Cursor > 0 && mText[Cursor] != '\n')
                                Cursor--;

                            // step off of '\n'
                            if (mText[Cursor] == '\n')
                                Cursor++;

                            while (Cursor < mText.Length &&
                                   mText[Cursor] != '\n' &&
                                   mCursorX > Panel.LabelFont.MeasureString(mText.Substring(0, Cursor).Split('\n').Last()).X)
                                Cursor++;
                        }
                    }
                    else if (lKey == Keys.Left)
                    {
                        Cursor--;
                        UpdateCursorX();
                    }
                    else if (lKey == Keys.Right)
                    {
                        Cursor++;
                        UpdateCursorX();
                    }
                    else if (lKey == Keys.Delete)
                    {
                        if (Cursor != mText.Length)
                            mText = mText.Remove(Cursor, 1);
                    }
                    else if (lKey == Keys.Back)
                    {
                        if (Cursor > 0)
                        {
                            mText = mText.Remove(Cursor - 1, 1);
                            Cursor--;
                        }
                        UpdateCursorX();
                    }
                }

                string lInsert = Game1.KeyBuffer.GetText();

                if (lInsert.Contains("\n"))
                {
                    lInsert = lInsert.Split('\n')[0];
                    mOnReturn(mText.Insert(Cursor, "" + lInsert.Replace('\r', '\n')));
                    Dispose = true;
                }

                mText = mText.Insert(Cursor, "" + lInsert.Replace('\r', '\n'));
                Cursor += lInsert.Length;
            }

            mPreviousKeys = lKeys;
        }

        public void Draw(SpriteBatch pSpriteBatch)
        {
            Size = new Vector2(Panel.LabelFont.MeasureString(mText).X + 60, (int)Math.Round(Panel.LabelFont.MeasureString(mText).Y / 40) * 40 + 40);

            TopLeft = Camera.Instance.ScreenSize / 2 - Size / 2;
            bool lBig = Size.X > 120;

            #region draw box

            // top left
            pSpriteBatch.Draw(sTextEditorTexture,
                              TopLeft + new Vector2(0, 0),
                              new Rectangle(0, 0, lBig ? 100 : 60, 20),
                              Color.White,
                              0,
                              Vector2.Zero,
                              new Vector2(1, 1),
                              SpriteEffects.None,
                              0f);

            // top edge
            pSpriteBatch.Draw(sTextEditorTexture,
                              TopLeft + new Vector2(lBig? 100 : 60, 0),
                              new Rectangle(100, 0, 20, 20),
                              Color.White,
                              0,
                              Vector2.Zero,
                              new Vector2(Size.X / 20f - (lBig ? 6 : 4), 1),
                              SpriteEffects.None,
                              0f);

            // top right
            pSpriteBatch.Draw(sTextEditorTexture,
                              TopLeft + new Vector2(Size.X - 20, 0),
                              new Rectangle(120, 0, 20, 20),
                              Color.White,
                              0,
                              Vector2.Zero,
                              new Vector2(1, 1),
                              SpriteEffects.None,
                              0f);

            // left edge
            for (int i = 20; i < Size.Y - 20; i += 40)
            pSpriteBatch.Draw(sTextEditorTexture,
                              TopLeft + new Vector2(0, i),
                              new Rectangle(0, 20, 40, 40),
                              Color.White,
                              0,
                              Vector2.Zero,
                              new Vector2(1, 1),
                              SpriteEffects.None,
                              0f);

            // middle
            pSpriteBatch.Draw(sTextEditorTexture,
                              TopLeft + new Vector2(40, 20),
                              new Rectangle(40, 20, 20, 20),
                              Color.White,
                              0,
                              Vector2.Zero,
                              new Vector2(Size.X / 20f - 3, Size.Y / 20f - 2),
                              SpriteEffects.None,
                              0f);

            // right edge
            pSpriteBatch.Draw(sTextEditorTexture,
                              TopLeft + new Vector2(Size.X - 20, 20),
                              new Rectangle(40, 20, 20, 20),
                              Color.White,
                              0,
                              Vector2.Zero,
                              new Vector2(1, Size.Y / 20f - 2),
                              SpriteEffects.None,
                              0f);

            // bottom left
            pSpriteBatch.Draw(sTextEditorTexture,
                              TopLeft + new Vector2(0, Size.Y - 20),
                              new Rectangle(0, 60, lBig ? 100 : 60, 20),
                              Color.White,
                              0,
                              Vector2.Zero,
                              new Vector2(1, 1),
                              SpriteEffects.None,
                              0f);

            // bottom edge
            pSpriteBatch.Draw(sTextEditorTexture,
                              TopLeft + new Vector2(lBig ? 100 : 60, Size.Y - 20),
                              new Rectangle(100, 60, 20, 20),
                              Color.White,
                              0,
                              Vector2.Zero,
                              new Vector2(Size.X / 20f - (lBig ? 6 : 4), 1),
                              SpriteEffects.None,
                              0f);

            // bottom left
            pSpriteBatch.Draw(sTextEditorTexture,
                              TopLeft + new Vector2(Size.X - 20, Size.Y - 20),
                              new Rectangle(120, 60, 20, 20),
                              Color.White,
                              0,
                              Vector2.Zero,
                              new Vector2(1, 1),
                              SpriteEffects.None,
                              0f);

            #endregion

            string lDisplayText = mText;

            int lBoxHeight = (int)Math.Round((Size.Y - 20) / Panel.LabelFont.LineSpacing);

            pSpriteBatch.DrawString(Panel.LabelFont, lDisplayText, mTopLeft + new Vector2(35, mSize.Y / 2 - Panel.LabelFont.LineSpacing * lBoxHeight / 2f), Color.White);
            for (int i = 0; i < lBoxHeight; i++)
            {
                pSpriteBatch.DrawString(Panel.LabelFont,
                                        (i + 1).ToString("00"),
                                        mTopLeft + new Vector2(10, mSize.Y / 2 - Panel.LabelFont.LineSpacing * lBoxHeight / 2f + Panel.LabelFont.LineSpacing * i),
                                        i % 2 == 0 ? Color.Cyan : Color.DarkCyan);
            }

            #region draw cursor

            string[] lTextBeforeCursor = mText.Substring(0, mCursor).Split('\n');
            int lX = (int)Panel.LabelFont.MeasureString(lTextBeforeCursor[lTextBeforeCursor.Length - 1]).X;
            int lY = (lTextBeforeCursor.Length - 1) * Panel.LabelFont.LineSpacing + 2;

            pSpriteBatch.Draw(sCursorTexture,
                              mTopLeft + new Vector2(35 + lX, mSize.Y / 2 - Panel.LabelFont.LineSpacing * lBoxHeight / 2f + lY),
                              Color.White);

            #endregion
        }

        public void UpdateCursorX()
        {
            mCursorX = Panel.LabelFont.MeasureString(mText.Substring(0, mCursor).Split('\n').Last()).X;
        }

        public void OnExit()
        {
        }

        public void OnEnter()
        {
        }

        public void OnClick(Vector2 pLocation)
        {
        }

        public void OnRelease(Vector2 pLocation)
        {
        }
    }
}